#include "fxcc/core/graph/directX11/Texture2DJack.h"
#include "fxcc/core/graph/directX11/RenderStates.h"
#include "fxcc/core/graph/directX11/WICTextureLoader.h"
#include "fxcc/core/graph/directX11/DDSTextureLoader.h"

using namespace fxcc::graph;
using namespace fxcc::graph::directX11;

directX11::Texture2DJack::Texture2DJack(const common::Texture2DJack::Desc& desc) : fxcc::graph::common::Texture2DJack(desc)
{
    Load();
};

void directX11::Texture2DJack::Load() 
{
    m_Available = Init();
}

bool directX11::Texture2DJack::Init()
{
    switch (m_Desc.m_Type)
    {
    case Desc::Type::_DDS_:
        return InitDDS();
    case Desc::Type::_WIC_:
        return InitWIC();
    case Desc::Type::_AllOC_:
        return InitAlloc();
    default:
        return false;
    }
}

bool fxcc::graph::directX11::Texture2DJack::InitAlloc()
{
    m_Desc.m_MipmapData.AllocData();
    return CreateFromImage(m_Desc.m_MipmapData);
}

bool fxcc::graph::directX11::Texture2DJack::InitDDS()
{
    auto device = directX11::RenderStates::Inst().Device();

    std::string path = m_Desc.m_DDSPath;
    std::wstring wpath(path.begin(), path.end());

    HRESULT hr = DirectX::CreateDDSTextureFromFile(device, wpath.c_str(), reinterpret_cast<ID3D11Resource**>(m_Texture2D.GetAddressOf()), m_ShaderResourceView.GetAddressOf());

    if (FAILED(hr))
    {
        ztclog::info("failed create texture 2d");
        return false;
    }
    return true;
}

bool fxcc::graph::directX11::Texture2DJack::InitWIC()
{
    auto device = RenderStates::Inst().Device();

    auto image = std::make_shared<fxcc::graph::common::ImageMipmap>(m_Desc.m_WICConfig);

    return CreateFromImage(image->m_WICTexture);
}

void fxcc::graph::directX11::Texture2DJack::VSSet(int startSlot, int numSlot)
{
    auto context = directX11::RenderStates::Inst().Context();
    context->VSSetShaderResources(startSlot, numSlot, m_ShaderResourceView.GetAddressOf());

}

void fxcc::graph::directX11::Texture2DJack::PSSet(int startSlot, int numSlot)
{
    auto context = directX11::RenderStates::Inst().Context();
    context->PSSetShaderResources(startSlot, numSlot, m_ShaderResourceView.GetAddressOf()); 


}
bool fxcc::graph::directX11::Texture2DJack::CreateFromImage(const fxcc::graph::common::ImageMipmap::MipMapData& mipMapData)
{
    auto device = RenderStates::Inst().Device();

    const auto& imageInfo = mipMapData;

    D3D11_TEXTURE2D_DESC textureDesc = {};
    textureDesc.Width = imageInfo.m_Width;
    textureDesc.Height = imageInfo.m_Height;
    textureDesc.MipLevels = 1;
    textureDesc.ArraySize = 1;
    textureDesc.SampleDesc.Count = 1;

    DXGI_FORMAT imageFormat = directX11::RenderStates::Inst().GetFormat(imageInfo.m_NrComponent, imageInfo.m_HDR);
    textureDesc.Format = imageFormat;

    if (m_Desc.m_DepthStencilMap)
    {
        textureDesc.Format = DXGI_FORMAT_R24G8_TYPELESS;
    }

    textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;

    if (m_Desc.m_DepthStencilMap)
        textureDesc.BindFlags |= D3D11_BIND_DEPTH_STENCIL;

    if (m_Desc.m_CreateRTV)
        textureDesc.BindFlags |= D3D11_BIND_RENDER_TARGET;

    if (m_Desc.m_CreateRTV || m_Desc.m_DepthStencilMap)
        textureDesc.Usage = D3D11_USAGE_DEFAULT;
    else
        textureDesc.Usage = D3D11_USAGE_IMMUTABLE;

    textureDesc.CPUAccessFlags = 0;
    textureDesc.MiscFlags = 0;

    D3D11_SUBRESOURCE_DATA initialData = {};
    initialData.pSysMem = mipMapData.GetData();
    initialData.SysMemPitch = mipMapData.RowPitch();
    initialData.SysMemSlicePitch = 0;
    HRESULT hr = device->CreateTexture2D(&textureDesc, &initialData, m_Texture2D.GetAddressOf());

    if (FAILED(hr)) {

        ztclog::info("Failed create texture create rtv %d depthStencil %d", m_Desc.m_CreateRTV, m_Desc.m_DepthStencilMap);
        return false;
    }

    D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
    srvDesc.Format = textureDesc.Format;
    if(m_Desc.m_DepthStencilMap)
        srvDesc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;

    srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    srvDesc.Texture2D.MostDetailedMip = 0;
    srvDesc.Texture2D.MipLevels = 1;

    hr = device->CreateShaderResourceView(m_Texture2D.Get(), &srvDesc, m_ShaderResourceView.ReleaseAndGetAddressOf());
    if (FAILED(hr)) {
        ztclog::info("Failed create texture shader resource view  %d depthStencil %d", m_Desc.m_CreateRTV, m_Desc.m_DepthStencilMap);
        return false;
    }

    if (m_Desc.m_CreateRTV)
    {
        hr = device->CreateRenderTargetView(m_Texture2D.Get(), nullptr, m_RenderTargetView.ReleaseAndGetAddressOf());
        if (FAILED(hr))
        {
            ztclog::info("failed create Render Target View");
            return false;
        }
    }

    if (m_Desc.m_DepthStencilMap)
    {
        D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
        dsvDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
        dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
        dsvDesc.Texture2D.MipSlice = 0;

        hr = device->CreateDepthStencilView(m_Texture2D.Get(), &dsvDesc, m_DepthStencilView.GetAddressOf());
        if (FAILED(hr))
        {
            ztclog::info("failed create depth Stencil View");
            return false;
        }
    }

    return true;
}
